#include "framework.h"
#include <sstream>
#include <algorithm>

Framework::Framework(){
	this->setWindowDimensions(800,800,0,0);
	//this->setWindowDimensions(1024,1024,0,0);
	this->setCaption("");
	this->unsetTimer();
	this->_fpsCalc=true;
	this->_fps=0;
	this->timebase=0;
	this->time=0;
	this->fscreen=false;
}

Framework::~Framework(){
}

Framework& Framework::instance(){
	static Framework framework;
	return framework;
}

void Framework::display(){
	Framework::instance().getCallbacks()->redisplay();

	///
	glFlush();
	glutSwapBuffers();
	///
}

void Framework::reshape(int w, int h){
	Framework::instance().getCallbacks()->reshape(Framework::instance().windowWidth,Framework::instance().windowHeight,w,h);
	Framework::instance().windowHeight = h;
	Framework::instance().windowWidth = w;
}

void Framework::keyboard(unsigned char key,int x,int y){
	Framework::instance().getCallbacks()->keyboard(key,x,y);
}

void Framework::specialFunc(int key,int x ,int y){
	Framework::instance().getCallbacks()->specKeyboard(key,x,y);
}

void Framework::mouseFunc(int button,int state,int x ,int y){
	Framework::instance().getCallbacks()->mousePressed(button,state,x,y);
}

void Framework::mouseMotion(int x ,int y){
	Framework::instance().getCallbacks()->mouseMotion(x,y);
}

void Framework::mouseEntryFunc(int state){
	Framework::instance().getCallbacks()->mouseEntry(state);
}

void Framework::mousePassiveMotion(int x,int y){
	Framework::instance().getCallbacks()->mousePassiveMotion(x,y);
}

void Framework::timer(int value){
	Framework& fr = Framework::instance();

	if(fr._fpsCalc){
		fr.frame++;
		fr.time=glutGet(GLUT_ELAPSED_TIME);

		if (fr.time - fr.timebase > 1000) {
			fr._fps = (fr.frame*1000.0/(fr.time-fr.timebase));
			fr.timebase = fr.time;
			fr.frame = 0;
		}
	}


	fr.getCallbacks()->timer(value);
	if(fr.isTimerSet())
		glutTimerFunc(fr.getTimerMsec(),Framework::timer,1);
}

bool Framework::isTimerSet(){
	return timerSet;
}

unsigned int Framework::getTimerMsec(){
	return msecTimer;
}


void Framework::setWindowDimensions(int windowHeigth,int windowWidth,int windowX,int windowY){
	this->windowHeight = windowHeigth;
	this->windowWidth = windowWidth;
	this->windowX = windowX;
	this->windowY = windowY;
}

void Framework::setClearColor(float r,float g,float b){
	glClearColor(r,g,b,0.0);
}

void Framework::setCaption(const std::string& caption){
	this->caption = caption;
}

void Framework::setTimer(unsigned int msec){
	this->timerSet = true;
	this->msecTimer = msec;
}

void Framework::unsetTimer(){
	this->timerSet = false;
}

void Framework::setCallbacks(IFrameworkCallbacks* callbacks){
	this->callbacks = callbacks;
}

void Framework::redisplay(){
	glutPostWindowRedisplay(this->window);
}

int Framework::getWindow(){
	return this->window;
}

float Framework::getWindowWidth(){
	return this->windowWidth;
}

float Framework::getWindowHeight(){
	return this->windowHeight;
}

void Framework::fpsCalc(bool state){
	_fpsCalc = state;
}

bool Framework::fullscreen(){
	return fscreen;
}

bool Framework::fpsCalc(){
	return _fpsCalc;
}

float Framework::fps(){
	return _fps;
}

void Framework::mainLoop(int argc,char** argv){
	getOptions(argc,argv);

	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

	glutInitWindowSize (windowWidth, windowHeight);
	glutInitWindowPosition (windowX, windowY);

	if(fscreen){
		std::ostringstream stream;
		stream << this->windowWidth << "x" << this->windowHeight << ":8@65";

		glutGameModeString( stream.str().c_str() );
		// start fullscreen game mode
		window = glutEnterGameMode();

		int w=glutGet(GLUT_SCREEN_WIDTH);
		int h=glutGet(GLUT_SCREEN_HEIGHT);
		this->setWindowDimensions(h,w,0,0);
	}else{
		window = glutCreateWindow (caption.c_str());
	}

	callbacks->init();
	glutKeyboardFunc(Framework::keyboard);
	glutDisplayFunc(Framework::display);
	glutSpecialFunc(Framework::specialFunc);
	glutReshapeFunc(Framework::reshape);
	glutMouseFunc(Framework::mouseFunc);
	glutEntryFunc(Framework::mouseEntryFunc);
	glutMotionFunc(Framework::mouseMotion);
	glutPassiveMotionFunc(Framework::mousePassiveMotion);
	glutIdleFunc(NULL);

	if(isTimerSet())
		glutTimerFunc(msecTimer,Framework::timer,1);

	glutMainLoop();
}

void Framework::exit(){
	if(fscreen)
		glutLeaveGameMode();
	glutLeaveMainLoop();
}

std::string Framework::getPath(){
	return this->path;
}

void Framework::getOptions(int argc,char** argv){
	OptionsParser p;
	p.fillParameters(argc,argv);
	fscreen = p.isInParameters("--full-screen");

	if(p.isInParameters("--screen-res")){
		std::string val;
		p.getParamValue("--screen-res",val);

		std::istringstream stream1(val.substr(0,val.find_first_of("x")));
		std::istringstream stream2(val.substr(val.find_first_of("x")+1,val.size()));

		stream1 >> this->windowWidth;
		stream2 >> this->windowHeight;
	}
}

/**
 * busca en el vector de parameters el elemento tag
 * si lo encuentra devuelve true, sino false
 */
bool OptionsParser::isInParameters(std::string tag) {
        return std::find(parameters.begin(),parameters.end(),tag)!= parameters.end();
}


/**
 * Llena los parametros pasados por el main.cpp en el vector parameters
 * luego para buscar o aplicar algoritmos se pueden usar los templates de las stl
 */
void OptionsParser::fillParameters(int argc,char** argv) {
        parameters = std::vector<std::string>();

        for( int i =1; i< argc; i++) {
                parameters.push_back(std::string(argv[i]));
        }
}

void OptionsParser::getParamValue(std::string tag,std::string& val){
	std::vector<std::string>::iterator it = std::find(parameters.begin(),parameters.end(),tag);
	++it;
	val = *it;
}
